package org.tau.workshop2011.semantics

import org.tau.workshop2011.parsing.AST._
import collection.mutable.{HashSet => MutableHashSet, Set => MutableSet, Map => MutableMap, HashMap => MutableHashMap}
import org.tau.workshop2011.expressions.Type

/**
 * This class encapsulates the results of all the type/symbol analysis that
 * happens before the code generation
 */

object SemanticAnalysis {

  def RVs2str (iter: Iterable[ResolvedVariable]) : String = {
    iter.toList.map { (rv) => rv.globalName } mkString(", ")
  }

  def debugPrintGST(gst: GlobalSymbolTable, indent:String) {
    println (indent + "Global symbol table:")
    for ((globalName, rv) <- gst.localMapping) {
      debugPrintRV(rv, indent + "  ")
      println()
    }
  }

  def debugPrintDeps(d: DependencyTracker[ResolvedVariable], indent: String) {
    println (indent + "Dependency analysis results:")
    for (v <- d.dependencySortedVars) {
      println (indent + "  " + v.globalName + " depends on " + RVs2str (d(v)))
    }
    println()
  }

  def debugPrintRV (rv: ResolvedVariable, indent: String) {
    println (indent + "Global Name: " + rv.globalName)
    println (indent + "Local Name: " + rv.definition.leftSide.id)
    println (indent + "Defined as: " + (rv.definition match {
      case definition:ExpressionAttribute => definition.realValue
      case definition:InitialAttribute => "?" + (definition.value match { case Some(value) => "(" + value + ")"; case None => "" })
    }))
    println (indent + "Resolved type: " + rv.definition.getLeftType)
  }

  abstract class WidgetAnalysis {
    val rootWidget:Widget
    val gst : GlobalSymbolTable
    val dependencies:DependencyTracker[ResolvedVariable]
    val readOnlyProps:MutableSet[ResolvedVariable]

    /* *** Expression Reversing Information *** */
    val dependOnConditionals:MutableSet[ResolvedVariable]
    val dependOnFunctionCalls:MutableSet[ResolvedVariable]
    val freeVariables:Set[ResolvedVariable]

    val integerExpansions:MutableMap[ResolvedVariable, (Option[LinearExpansion], ReadOnlyStatus)]
    val booleanExpansions:MutableMap[ResolvedVariable, (Option[BoolExpansion], ReadOnlyStatus)]

    /* This will hold mappings of variables (which are not integer/boolean type)
     * to another optional variable. If that option isn't none, but a real
     * variable, then that is the free variable that should get the value of the
     * key variable when that is updated
     */
    val otherExpansions:MutableMap[ResolvedVariable, Option[ResolvedVariable]]


    def debugPrint(indent: String = "") {
      println(indent + "The root widget is")
      println(indent + "  " + rootWidget)
      println()

      debugPrintGST(gst, indent)

      debugPrintDeps(dependencies, indent)

      println(indent + "The read-only properties are:")
      println(indent + "  " + RVs2str(readOnlyProps))

      if (! booleanExpansions.isEmpty) {
        println()
        println("The status of boolean expressions is:")
        for ((rv, (be, ros)) <- booleanExpansions) {
          println(rv + ": " + be + " (" + ros + ")")
        }
      }

      if (! (integerExpansions.isEmpty)) {
        println()
        println("The status of integer expressions is:")
        for ((rv, (be, ros)) <- integerExpansions) {
          println(rv + ": " + ((be, ros) match {
            case (Some(expr), ReadOnlyStatus.ReadAndWrite) => expr
            case (Some(expr), ReadOnlyStatus.NothingToUpdate) => expr + " (A constant value)"
            case (Some(expr), _) => assert(false)
            case (None, reason) => "Read only - " + reason
          }))
        }
      }

      if (! otherExpansions.isEmpty) {
        println()
        println("The status of other expressions is:")
        for ((rv, be) <- otherExpansions) {
          println(rv + ": " + (be match {
            case None => "Read only property"
            case Some(v) => "Matches exactly the free variable " + v
          }))
        }
      }
    }
  }
}

class SemanticAnalysis(program:Program) {
  val analysis:MutableMap[String, SemanticAnalysis.WidgetAnalysis] = new MutableHashMap

  for ((name, widget) <- program.defs) {
    val (rw, st) = SymbolResolvingVisitor.analyze(widget)

    val pa = new SemanticAnalysis.WidgetAnalysis {
      override val rootWidget: Widget = rw
      override val gst: GlobalSymbolTable = st
      override val dependencies: DependencyTracker[ResolvedVariable] = DependencyResolver.resolveDependencies(rootWidget)
      override val readOnlyProps: MutableSet[ResolvedVariable] = new MutableHashSet
      override val dependOnConditionals: MutableSet[ResolvedVariable] = new MutableHashSet[ResolvedVariable]
      override val dependOnFunctionCalls: MutableSet[ResolvedVariable] = new MutableHashSet[ResolvedVariable]

      override val freeVariables: Set[ResolvedVariable] = dependencies.getVars filter {
        (rv:ResolvedVariable) => rv.definition.isInstanceOf[InitialAttribute]
      }

      override val integerExpansions: MutableMap[ResolvedVariable, (Option[LinearExpansion], ReadOnlyStatus)] = new MutableHashMap
      override val booleanExpansions: MutableMap[ResolvedVariable, (Option[BoolExpansion], ReadOnlyStatus)] = new MutableHashMap
      override val otherExpansions: MutableMap[ResolvedVariable, Option[ResolvedVariable]] = new MutableHashMap
    }

    TypeResolver.resolveTypes(pa)

    for (rv <- pa.dependencies.dependencySortedVars) {
      var isConditional = false
      var isFunctional = false
      rv accept new ASTDeepVisitor {
        override def visit(node: Condition): Any = {
          isConditional = true
          super.visit(node)
        }

        override def visit(node: FunctionCall): Any = {
          isFunctional = true
          super.visit(node)
        }

        override def visit(node: Variable): Any = {
          if (pa.dependOnConditionals contains (ResolvedVariable from node))
            isConditional = true
          if (pa.dependOnFunctionCalls contains (ResolvedVariable from node))
            isFunctional = true
          super.visit(node)
        }
      }

      if (isConditional)
        pa.dependOnConditionals add rv
      if (isFunctional)
        pa.dependOnConditionals add rv
    }

    val intE = new IntegerExpressionExpander(pa)
    val boolE = new BooleanExpressionExpander(pa)

    for (rv <- pa.dependencies.dependencySortedVars) {
      if (rv.definition.getLeftType == Type.tInt) {
        val (expansion, ros) = intE.expand(rv.definition)
        pa.integerExpansions += rv -> ((expansion, ros))
        if (ros.isReadOnly)
          pa.readOnlyProps += rv
      } else if (rv.definition.getLeftType == Type.tBoolean) {
        val (expansion, ros) = boolE.expand(rv.definition)
        pa.booleanExpansions += rv -> ((expansion, ros))
        if (ros.isReadOnly)
          pa.readOnlyProps += rv
      } else if (rv.definition == pa.gst.globalWidth || rv.definition == pa.gst.globalHeight) {
        /* Important!!! */
        /* Important!!! */
        /* Set the global width and height to read only */
        /* Important!!! */
        /* Important!!! */
        pa.readOnlyProps += rv
      } else {
        val theVar = rv.getVariable
        rv.definition match {
          /* An initial attribute isn't read-only */
          case _:InitialAttribute => {
            pa.otherExpansions += rv -> Some(rv)
          }
          /* If a variable is defined by another variable, then it's read-only
           * status and reverse-expression are the same
           */
          case ExpressionAttribute(`theVar`, v:Variable) => {
            if (pa.readOnlyProps contains v)
              pa.readOnlyProps += rv
            pa.otherExpansions += rv -> pa.otherExpansions(v)
          }
            /* Reversing of other non int and non boolean properties isn't
             * supported
             */
          case _ => {
            pa.readOnlyProps += rv
            pa.otherExpansions += rv -> None
          }
        }
      }
    }

    analysis += name -> pa
  }

  def debugPrint() {
    for ((name,wa) <- analysis) {
      println ("Printing analysis of top-level widget " + name)
      wa.debugPrint("  ")
      println()
    }
  }

}